﻿#ifndef __INC_QG_GL_RES_H__
#define __INC_QG_GL_RES_H__

#ifdef _MSC_VER
#pragma once
#endif

#include "qgp/qgacq.h"
#include "qgp/qgacu.h"

//
class GLRdh;
class GLDhslState;
class GLRaszState;
class GLBlndState;
class GLSplrState;
class GLShader;
class GLLayout;
class GLBuffer;
class GLDepthBuffer;
class GLTexture;
class GLTextureMotion;
class GLTextureOff;


//////////////////////////////////////////////////////////////////////////
// 뎁스 스텐실

/// 뎁스 스텐실 프로퍼티
struct GLDhslProperty
{
	GLboolean		zenable;
	GLboolean		zwrite;
	GLenum			zfunc;

	bool			stencil;
	bool			twoside;
	GLenum			front_func;
	GLenum			front_fail;
	GLenum			front_zfail;
	GLenum			front_pass;
	GLenum			back_func;
	GLenum			back_fail;
	GLenum			back_zfail;
	GLenum			back_pass;
	kuint			mask_read;
	kuint			mask_write;

	GLDhslProperty()
		: zenable(GL_TRUE), zwrite(GL_TRUE), zfunc(GL_LEQUAL)
		, stencil(false), twoside(false)
		, front_func(QGGL_INVALID_ENUM), front_fail(QGGL_INVALID_ENUM)
		, front_zfail(QGGL_INVALID_ENUM), front_pass(QGGL_INVALID_ENUM)
		, back_func(QGGL_INVALID_ENUM), back_fail(QGGL_INVALID_ENUM)
		, back_zfail(QGGL_INVALID_ENUM), back_pass(QGGL_INVALID_ENUM)
		, mask_read(QGGL_INVALID_UINT), mask_write(QGGL_INVALID_UINT)
	{
	}
};

// 뎁스 스텐실
class GLDhslState : public qgDhslState
{
	KCH_AUTOCLASS(GLDhslState, qgDhslState, "GLDhslState");
	KCH_FRIEND(GLRdh);

private:
	GLDhslProperty		_prop;

private:
	GLDhslState() {};
};


//////////////////////////////////////////////////////////////////////////
// 래스터라이저

// 래스터라이저 프로퍼티
struct GLRaszProperty
{
	GLenum				fill;
	GLenum				cull;

	float				depth_bias;
	float				slope_scale;

	GLRaszProperty()
		: fill(GL_FILL), cull(GL_BACK), depth_bias(0.0f), slope_scale(0.0f)
	{
	}
};

// 래스터라이저
class GLRaszState : public qgRaszState
{
	KCH_AUTOCLASS(GLRaszState, qgRaszState, "GLRaszState");
	KCH_FRIEND(GLRdh);

private:
	GLRaszProperty		_prop;

private:
	GLRaszState() {};
};


//////////////////////////////////////////////////////////////////////////
// 블렌드

// 블렌드 프로퍼티
struct GLBlndProperty
{
	bool			enable;
	bool			separate;
	GLenum			color_op;
	GLenum			color_src;
	GLenum			color_dst;
	GLenum			alpha_op;
	GLenum			alpha_src;
	GLenum			alpha_dst;
	GLboolean		mask_r : 1;
	GLboolean		mask_g : 1;
	GLboolean		mask_b : 1;
	GLboolean		mask_a : 1;

	GLBlndProperty()
		: enable(false), separate(false)
		, color_op(GL_FUNC_ADD), color_src(GL_ONE), color_dst(GL_ZERO)
		, alpha_op(GL_FUNC_ADD), alpha_src(GL_ONE), alpha_dst(GL_ZERO)
		, mask_r(GL_TRUE), mask_g(GL_TRUE), mask_b(GL_TRUE), mask_a(GL_TRUE)
	{
	}
};

// 블렌드
class GLBlndState : public qgBlndState
{
	KCH_AUTOCLASS(GLBlndState, qgBlndState, "GLBlndState");
	KCH_FRIEND(GLRdh);

private:
	GLBlndProperty		_prop[QGSGOFF_MAX_VALUE];

private:
	GLBlndState() {};
};


//////////////////////////////////////////////////////////////////////////
// 샘플러
class GLSplrState : public qgSplrState
{
	KCH_AUTOCLASS(GLSplrState, qgSplrState, "GLSplrState");
	KCH_FRIEND(GLRdh);

public:
	struct Info
	{
		GLint			mag_filter;
		GLint			min_filter;
		GLint			wrap_s;
		GLint			wrap_t;
		GLint			wrap_r;
		GLint			cop_mode;
		GLint			cop_func;
		GLint			lod_bias;
	};

private:
	Info				_info;

private:
	GLSplrState() {};
};


//////////////////////////////////////////////////////////////////////////
// 세이더
class GLShader : public qgShader
{
	KCH_AUTOCLASS(GLShader, qgShader, "GLShader");
	KT_DECL_TYPE_INIT(GLShader);
	KCH_FRIEND(GLRdh);
	KCH_FRIEND(GLLayout);

public:
	struct RefHandle : public tpl::MemAlloc<>
	{
		kint			ref;
		GLuint			handle;

		RefHandle(GLuint h) : ref(1), handle(h) {}
	};

	struct AttribInfo
	{
		GLint				attrib;

		qgShaderData		data : 16;
		kuint				size : 16;

		qgDloUsage			usage : 16;
		kint				index : 16;	// usage 인덱스

		ksize_t				hash;
		char				name[32];

		AttribInfo*			next;
	};

	typedef tpl::vector<qbShaderVar> VarArray;
	typedef tpl::vector<AttribInfo> AttribArray;

private:
	RefHandle*			_rfp;
	RefHandle*			_rvp;

	VarArray			_vars;
	AttribArray			_atrs;

	kint				_amask;
	kint				_acnt[QGDLOU_MAX_VALUE];
	AttribInfo*			_alnk[QGDLOU_MAX_VALUE];

	bool				_linked;

private:
	GLShader() throw();
	~GLShader() throw();

public:
	K_OVR bool BindFile(qgShaderType type, const char* filename, kint flags);
	K_OVR bool BindData(qgShaderType type, kconstpointer data, kint size, kint flags);
	K_OVR bool BindShader(qgShaderType type, qgShader* shd);
	K_OVR bool BindShader(qgShaderType type, const char* name);
	K_OVR bool Link();

	AttribInfo* FindAttribInfo(qgDloUsage usage, kint index) throw();

	void _ProcessMap() throw();

private:
	void _PrintError(GLuint handle, kint errcode, const char* target, bool shdinfo, bool prginfo) throw();

	RefHandle* _CompileProgram(qgShaderType type, const char* src, GLint len) throw();
	bool _BindData(qgShaderType type, kconstpointer data, kint size, int flags) throw();

	static void _RemoveHandle(RefHandle* rh, GLuint program) throw();

	static qgShaderData _GLTypeToShaderData(GLenum type) throw();
	static qgDloUsage _GLAttribToLayoutUsage(const char* name) throw();

	static void _AutoInitFunc();
	static kpointer _AutoMatchFunc(qbShaderVar& v);
};


//////////////////////////////////////////////////////////////////////////
// 레이아웃

// 레이아웃 프로퍼티
struct GLLayoutProperty
{
	bool			enable;
	kpointer		pointer;
	GLuint			buffer;
	GLsizei			stride;
	GLuint			size;
	GLenum			format;
	GLboolean		normalized;

	GLLayoutProperty()
		: enable(false)
		, pointer(NULL)
		, buffer(0)
		, stride(0)
		, size(0)
		, format(QGGL_INVALID_ENUM)
		, normalized(false)
	{
	}
};

// 레이아웃
class GLLayout : public qgVertexLayout
{
	KCH_SUBCLASS(GLLayout, qgVertexLayout);
	KCH_FRIEND(GLRdh);
	KCH_FRIEND(GLShader);

public:
	struct Element
	{
		qgDloUsage		usage;
		kuint			index;
		GLenum			format;
		GLuint			offset;
		GLuint			attrib;
		GLuint			size;
		GLboolean		normalized;
		GLboolean		convfloat;
	};

private:
	kint				_sg_cnt[QGSGVERT_MAX_VALUE];
	Element*			_sg_elm[QGSGVERT_MAX_VALUE];

private:
	GLLayout() throw();
	~GLLayout() throw();
};


//////////////////////////////////////////////////////////////////////////
// 버퍼
class GLBuffer : public qgBuffer
{
	KCH_SUBCLASS(GLBuffer, qgBuffer);
	KCH_FRIEND(GLRdh);

private:
	GLenum				_gl_type;
	GLenum				_gl_usage;
	bool				_immutable;

	kpointer			_lock;
	kpointer			_slockptr;

private:
	GLBuffer() throw();
	~GLBuffer() throw();

public:
	K_OVR kpointer Map(kint flags);
	K_OVR bool Unmap();
};


//////////////////////////////////////////////////////////////////////////
// 뎁스 버퍼
class GLDepthBuffer : public qgDepthBuffer
{
	KCH_SUBCLASS(GLDepthBuffer, qgDepthBuffer);
	KCH_FRIEND(GLRdh);
	KCH_FRIEND(GLTextureOff);

private:
	bool				_stencil;
	GLTextureOff*		_lastoff;

protected:
	GLDepthBuffer() throw();
	~GLDepthBuffer() throw();

public:
	GLTextureOff* GetLastAttach() { return _lastoff; }
	bool Attach(GLTextureOff* texoff);
};


//////////////////////////////////////////////////////////////////////////
// 텍스쳐

// 텍슷차 프로퍼티
struct GLTextureProperty
{
	GLenum				type;
	GLuint				id;
	GLint				miplvl;
	GLint				mipcnt;
};

// 텍스쳐
class GLTexture : public qgTexture
{
	KCH_SUBCLASS(GLTexture, qgTexture);
	KCH_FRIEND(GLRdh);

protected:
	qgImage*			_img_rsw;
	qgImage*			_img_mip;

	GLenum				_gl_type;
	GLenum				_gl_format;

	GLenum				_pixel_type;
	GLenum				_pixel_format;

	GLuint				_gl_width;
	GLuint				_gl_height;
	GLuint				_gl_depth;

	GLSplrState*		_splr;

protected:
	GLTexture() throw();
	~GLTexture() throw();

public:
	K_OVR bool Map(qbTextureMap* map, kint flag, kint mips, kint sliceface);
	K_OVR bool Unmap();

protected:
	bool _InitProperty(const char* name, qgImage* img, kuint mips, kint flags, GLRdh* rdh);
	bool _InitProperty(const char* name, qgTextureType type, kuint width, kuint height, kuint depth, kuint mips, kint flags, qgColorFormat fmt, GLRdh* rdh);
	bool _CreateInternal(GLuint* ptex, GLRdh* rdh);
	bool _CreateTexture(qgImage* img, GLRdh* rdh);
	void _UploadTexture(bool isnew, kint mipno, GLRdh* rdh);
};


//////////////////////////////////////////////////////////////////////////
// 모션 텍스쳐, 클래스의 이중 상속이 있다.
class GLTextureMotion : public GLTexture, public qbServAnimTexture
{
	KCH_SUBCLASS(GLTextureMotion, GLTexture);
	KCH_FRIEND(GLRdh);

private:
	GLTextureMotion() throw();
	~GLTextureMotion() throw();

public:
	K_OVR qbAnimTexture* GetMotion() { return this; }

	K_OVR bool MotionUpdate(float adv);

private:
	bool _CreateMotion(const char* filename, kint flags, GLRdh* rdh);
};


//////////////////////////////////////////////////////////////////////////
// 오프 텍스쳐
class GLTextureOff : public GLTexture, public qbOffTexture
{
	KCH_SUBCLASS(GLTextureOff, GLTexture);
	KCH_FRIEND(GLRdh);

private:
	GLuint						_fbo;
	GLDepthBuffer*				_db;

	qgTexture*					_save_tex;
	kint						_save_cnt;

private:
	GLTextureOff() throw();
	~GLTextureOff() throw();

public:
	K_OVR qbOffTexture* GetOff() { return this; }

	// qgITextureOff
	K_OVR bool Enter(kint clearance, const tpl::ColorValue& color, float depth, kuint stencil);
	K_OVR bool Leave();

private:
	bool _CreateOff(GLRdh* rdh);

public:
	static const char* CheckFBO();
};

#endif	// __INC_QG_GL_RES_H__
